{literal}

<h1>Introduction</h1>
Creating new property handlers is not that difficult as it might seem, but they need some unserstanding how DomAr works.
Here are given some samples of different property handlers. It would be quite impossible to tell every detail about property handlers so you should find out useful stuff from these samples.
<br />
When you can't get enough knowledge from these samples you should check DomAr basic properties and maybe even Relation property handlers (These are quite complex). 
All DomAr property handlers are created using one defined logic - NO exceptions.
<br /><br />
Also keep in mind that creating handlers may buzz your brain :). You need to think about many scenarios and <b>Unit testing</b> should be mandatory!
Some questions you should ask yourself:
<ul>
	<li>What happens if wrong/bad value is given</li>
	<li>What happens if value is null / empty </li>
	<li>What happens if value is omitted to another object? ($a->value = $b->value)</li>
	<li>What happens if value is serialized?</li>
	<li>What happens if value is deleted?
	<ul>
		<li>Very important for handlers that work with files. If object is deleted it should delete related files</li>
	</ul>
	</li>
	<li>Do i need that value always to be initialized if object is loaded from db?
	<ul>
		<li>You might not need complex objects created everytime. If thats true, use Init and Load listeners. They help to use LazyLoad pattern.</li>
	</ul>
	</li>
</ul>
<h1>Overview</h1>

Each property handler needs handling class. It is usually called DomArProperty_Something.<br>
This handling class is used to work with property value. 
Handler knows:<br />
1) How to insert it into database<br />
2) How to update it in database<br />
3) How to delete it<br />
4) How to load it<br />
5) How to alter table to hold it.<br />
<br />
Each property handler is accessible in class by <b>ClassName::getProperty($propertyName)</b> method.<br />

<h1>Simple text sample</h1>
Internally each class property has one handling class that handles its values.<br />
See this sample. (For more  listener see "<a href="{/literal}{$pageUrl}{literal}DomAr/propertyHandlerInterfaces">Property handler interfaces</a>" section)
<pre class="code">
&lt?php
/**
* @author Oliver Leisalu, Inditel Meedia
* @package DomAr
* @subpackage Property
*/
class DomArProperty_SimpleText extends DomArProperty implements 
								IDomArPropertyAlterTableListener,
								IDomArPropertyValidator,
								IDomArPropertyInsertListener,
								IDomArPropertyUpdateListener {
	

	public function __construct( $name ) {
		parent::__construct( $name );
	}
	
	/**
	* Validate if property value is allowed to have that value. We allow only strings.
	* 0 means that value is validated and can be assigned to property.
	* other numbers are error codes. See samples section validation error codes.
	*/
	public function validate( DomArObject $object, &$propertyValue, array &$customParams = array() ) {
		if( !is_string($propertyValue) ) 
			return 1;
		else
			return 0;
	}
	
	/**
	* When we call Item::getTable()->update() this method is expected to alter table the way it needs.
	*/
	public function alterTable( DomArTable $table ) {
		$table->changeColumn( $this->name, 'text', true );
	}
	
	/**
	* If object is first inserted into database alter insert query.
	* INSERT INTO table ($fieldNames) values ($fieldValues)
	* First letter must be always comma (',')!
	*/
	public function insert( DomArObject $object, &$propertyValue, &$fieldNames, &$fieldValues  ) {
		$fieldNames .= ',`'.$this->name.'`';
		$fieldValues .= ',\''.addslashes($propertyValue).'\'';
	}
	
	/**
	* If object is updated alter update query. Basicly add update fields.
	* UPDATE table SET $setValues WHERE id=$id
	* First letter must be always comma (',')!
	*/
	public function update( DomArObject $object, &$propertyValue, &$setValues ) {
		$setValues .= ',`'.$this->name.'` = \''.addslashes($propertyValue).'\'';
	}
	
}
?&gt;
</pre>

<h1>Container object sample</h1>
Usually when you need to create custom property handler your property value is not scalar - it is object or array. 
<h2>Code</h2>
<pre class="code">
&lt;?php
class PersonCode extends DomArPlugin {
	
	public $code
	
	public function __construct( $code = NULL ) {
		$this->set($code);
	}
	
	public function set( $code ) {
		$this->code = $code;
		// this tells parent that something is changed with this object and when save() is called it should save this.
		$this->changed();
	}
	
	public function getBirthday() {
		$year = substr($this->code, 1,2);
		$month = substr($this->code, 3,2);
		$day = substr($this->code, 5,2);
		return array(
			'year' => $year,
			'month' => $month,
			'day' => $day,
		);
	}
	
	/**
	* @return String [N for woman, M for man]
	*/
	public function getSex() {
		$c = substr($this->code,0,1);
		if( $c<=0 or $c>6) 
			return NULL;
		$code = $c%2==0 ? 'W' : 'M';
	}
	
	public function __toString() {
		return (string)$code;
	}
	
	
	
}


class DomArProperty_PersonCode extends DomArProperty_Plugin implements 
					IDomArPropertyAlterTableListener,
					IDomArPropertyUpdateListener,
					IDomArPropertyInsertListener,
					IDomArPropertyBeforeSetListener {
	

	public $defaultCode;

	public function __construct( $name, $defaultCode; ) {
		parent::__construct( $name );
		$this->defaultCode; = $defaultCode;
	}
	
	public function init( DomArObject $object, array &$values ) {
		return (string)$values[$this->name];
	}
	
	public function load( DomArObject $object, &$propertyValue ) {
		$r = new PersonCode( $propertyValue );
		// tell child object who is his parent and in which property it is held.
		$r->changeParent( $object, $this );
		return $r;
	}
	
	public function isLoaded( DomArObject $object, &$propertyValue ) {
		return is_object($propertyValue);
	}
	
	public function validate( DomArObject $object, &$propertyValue, array &$customParams = array() ) {
		if( strlen($propertyValue) != 11 )
			return 1;
		else
			return 0;
	}
	
	public function beforeSet( DomArObject $object, &$oldPropertyValue, &$newPropertyValue ) {
		if( !is_object($newPropertyValue) ) {
			$newPropertyValue = new PersonCode($newPropertyValue);
		}
		// tell child object who is his parent and in which property it is held.
		$newPropertyValue->changeParent( $object, $this );
		return $newPropertyValue;
	}
	
	public function insert( DomArObject $object, &$propertyValue, &$fieldNames, &$fieldValues ) {
		$fieldNames .= ',`'.$this->name.'`';
		$fieldValues .= ','.(int)$object->{$this->name}->pos;
	}
	
	public function update( DomArObject $object, &$propertyValue, &$values ) {
		$values .= ',`'.$this->name.'` = '.(int)$object->{$this->name}->pos;
	}

	public function alterTable( DomArTable $table ) {
		$table->changeColumn( $this->name, 'int(11)', true );
	}
	
}
?&gt;
</pre>

<h2>Using</h2>
<pre class="code">
&lt;?php
class Person extends DomArObject {

	/**
	* @orm char(255)
	*/
	public $name;
	
	/**
	* Default value is always 37605030299.
	* @orm plugin PersonCode(37605030299)
	*/
	public $code;

}

$person = new Person();
echo $person->code; // echoes 37605030299
$person->code = '47605030299'; // can be used this way thanks to beforeSet listener which changes string to object.
echo get_class($person->code); // echoes PersonCode
echo $person->code; // echoes 47605030299;
$person->code->set('57605030299');
echo $person->code; // echoes 57605030299;

// test failure
$person->code = 2;
echo $person->code; // echoes 57605030299 - previous value stays, but object cannot be saved as there is failed validaton.
echo $person->getValidationMessages('<br>'); // outputs validation error.
?&gt;
</pre>




<h1>Plugin source</h1>

Plugin is nothing magical, but they are very comfortable to use as you don't have to mess with annotations. 
All plugins annotations work with annotation @orm plugin PLUGIN_NAME(ARGUMENTS). Arguments will be given to DomArProperty_Plugin constructor.<br  />
Plugin source code:
<pre class="code">
&lt;?php
interface IDomArProperty_Plugin extends IDomArProperty, IDomArPropertyValidator, IDomArPropertyLoadListener, IDomArPropertyAlterTableListener {
	
	public function registerListener( $type, $method, array $args );
	
}

abstract class DomArProperty_Plugin extends DomArProperty implements IDomArProperty_Plugin{

	public function registerListener( $type, $method, array $args ) {
		throw new RuntimeException('<strong>'.get_class($this).'</strong> has no plugin listeners. Tried to add listener type <strong>'.$type.'</strong> which calls method <strong>'.$method.'</strong>');
	}
}

abstract class DomArPlugin {

	private $parent;
	private $property;

	public function __construct( DomArObject $parent = NULL, IDomArProperty_Plugin $property = NULL ) {
		$this->parent = $parent;
		$this->property = $property;
	}

	public function changeParent( DomArObject $parent = NULL, IDomArProperty_Plugin $property = NULL) {
		$this->parent = $parent;
		$this->property = $property;
	}

	final public function getParent() {
		return $this->parent;
	}
	
	final public function getProperty() {
		return $this->property;
	}
	
	final public function changed() {
		if( $this->parent !== NULL )
			$this->parent->changed( $this->property->name );
	}
}
</pre>

{/literal}